package ObjectOriented;

/**
 * 继承： 复用公共的代码
 * 通过 extends关键字来实现
 * 注意：1、Java中只支持单继承，一个类只能继承一个父类（只有Object类例外,没有父类）
         2、子类自动获得父类的所有属性和方法，包括私有属性和方法，因此严禁定义与父类同名的属性和方法
         3、子类无法访问父类的私有属性和方法，但可以通过父类的公有方法访问私有属性和方法
         4、可以将private改为protected，子类可以访问父类的protected属性和方法
 * 在OOP（面向对象）术语中，类B继承类A，类A称为父类（超类、基类），类B称为子类（派生类）

 * super关键字
   1、super关键字用于引用父类的属性和方法
   2、super关键字只能在子类的构造器中使用，在其他位置使用会报错
   3、super关键字可以调用父类的构造器，并传入参数
 * 
 * 注意：1、任何class的构造方法，第一行语句必须是调用父类的构造方法。如果没有明确地调用父类的构造方法，编译器会帮我们自动加一句super();
        2、如果父类没有无参构造方法，子类必须显式调用父类的构造方法，并传入参数
        3、如果子类没有显式调用父类的构造方法，编译器会自动加一句super();，但是如果父类没有无参构造方法，编译器会报错
        4、子类不会继承父类的构造方法，但是可以通过super()调用

  * 阻止继承： 从Java15开始，可以使用sealed关键字来定义一个类可以被哪些类继承，哪些类不能继承。衍生的类（如 Student 类）必须被声明为 final、sealed 或 non-sealed，以明确其子类化的行为。
              Java15想启用sealed关键字，需要使用--enable-preview参数和--source 15参数
              1、sealed关键字可以修饰class，表示这个类可以被哪些类继承
              2、final关键字可以修饰class，表示这个类不能被继承

    向上转型：父类引用指向子类对象，这种指向是安全的，因为子类继承了父类的所有方法
    向下转型：如果把一个父类类型强制转型为子类类型,需要使用instanceof判断是否是子类

    区分继承和组合：
    继承：子类是父类的一种特殊类型，子类可以覆写父类的方法，可以用子类替换父类，是is关系
    组合：一个类的字段包含另一个类的实例，通过调用实例的方法来实现自己的方法，是has关系
 */
public sealed class Extend permits Student {
  private String name;
  private int age;

  public Extend(String name, int age) {
    this.name = name;
    this.age = age;
  }
  public String getName() {
    return this.name;
  }
  public int getAge() {
    return this.age;
  }
  public static void main(String[] args) {
    // Student s = new Student("Xiao Ming", 12, 89);
    // System.out.println(s.getName());
    // System.out.println(s.getAge());

    /**
     * 向上转型：父类引用指向子类对象，这种指向是安全的，因为子类继承了父类的所有方法
     */
    Extend p = new Student("Mr Zhao", 32, 100);
    Extend p1 = new Extend("Mrs Han", 26);
    // System.out.println(p.getName());
    // System.out.println(p.getAge());

    /**
     * 向下转型：如果把一个父类类型强制转型为子类类型,需要使用instanceof判断是否是子类
     */
    // Student s2 = (Student) p1;
    // System.out.println(s2.getName());
    // System.out.println(s2.getAge());
    System.out.println(p1 instanceof Student);
    System.out.println(p instanceof Student);
    if (p1 instanceof Student) {
      Student s3 = (Student) p1;
      System.out.println(s3.getName());
      System.out.println(s3.getAge());
      System.out.println(s3.getScore());
    }

    // 组合
    Teacher t = new Teacher("Math", p1);
    System.out.println(t.subject);
    System.out.println(t.person.getName());
  }
}

/**
 * 在编程中，特别是使用 Java 等语言时，公共类（public class）应该放在与类名相同的文件中。
 * 比如，要定义一个公共类型 Student，你需要创建一个名为 Student.java 的文件。
 */
non-sealed class Student extends Extend {
  private int score;
  public Student(String name, int age, int score) {
    super(name, age);
    this.score = score;
  }
  public int getScore() {
    return this.score;
  }
}

class Teacher {
  public String subject;
  public Extend person;
  public Teacher(String subject, Extend person) {
    this.subject = subject;
    this.person = person;
  }
}